package net

import (
	"encoding/json"
	"errors"
	"github.com/forgoer/openssl"
	"github.com/gorilla/websocket"
	"github.com/mitchellh/mapstructure"
	"go.uber.org/zap"
	"log"
	myLog "mssgserver/log"
	"mssgserver/utils"
	"runtime/debug"
	"sync"
	"time"
)

/* 优化点
* 可以加上token登录鉴权
* 设计服务端心跳机制
* 配合 SessionServer 构建多节点路由转发结构
* 对 管道加一个缓冲 "对无缓冲通道而言，发送者必须等到接收者准备好接收数据，才能完成发送（否则阻塞）"
					“对于有缓冲 当通道没满，可以直接放进去，不阻塞  只有缓冲满了才会阻塞发送者”
*/

// websocket核心结构
type wsServer struct {
	wsConn       *websocket.Conn
	router       *Router
	outChan      chan *WsMsgRsp // 无缓冲会阻塞写线程
	Seq          int64
	property     map[string]interface{}
	propertyLock sync.RWMutex
	needSecret   bool
}

// 注入路由器（注册路由）
func (w *wsServer) Router(router *Router) {
	w.router = router
}

// 属性设置/读取/移除（支持并发安全）
func (w *wsServer) SetProperty(key string, value interface{}) {
	w.propertyLock.Lock()
	defer w.propertyLock.Unlock()
	w.property[key] = value
}
func (w *wsServer) GetProperty(key string) (interface{}, error) {
	w.propertyLock.RLock()
	defer w.propertyLock.RUnlock()
	if value, ok := w.property[key]; ok {
		return value, nil
	} else {
		return nil, errors.New("no property found")
	}
}
func (w *wsServer) RemoveProperty(key string) {
	w.propertyLock.Lock()
	defer w.propertyLock.Unlock()
	delete(w.property, key)
}

// 获取客户端 IP 地址
func (w *wsServer) Addr() string {
	return w.wsConn.RemoteAddr().String()
}

// 主动向客户端推送一条消息
func (w *wsServer) Push(name string, data interface{}) {
	rsp := &WsMsgRsp{Body: &RspBody{Name: name, Msg: data, Seq: 0}}
	w.outChan <- rsp
}

// 分别启动读循环和写循环两个 goroutine，实现全双工处理
func (w *wsServer) Start() {
	//启动读写数据的处理逻辑
	go w.readMsgLoop()
	go w.writeMsgLoop()
}

// 写消息流程
func (w *wsServer) writeMsgLoop() {
	for {
		select {
		case msg := <-w.outChan:
			w.Write(msg.Body)
		}
	}
}

// 加密、压缩、发送
func (w *wsServer) Write(msg interface{}) {
	data, err := json.Marshal(msg)
	if err != nil {
		log.Println(err)
	}
	secretKey, err := w.GetProperty("secretKey")
	if err == nil {
		//有加密
		key := secretKey.(string)
		//数据做加密
		data, _ = utils.AesCBCEncrypt(data, []byte(key), []byte(key), openssl.ZEROS_PADDING)
	}
	//压缩
	if data, err := utils.Zip(data); err == nil {
		err := w.wsConn.WriteMessage(websocket.BinaryMessage, data)
		if err != nil {
			log.Println("服务端写数据出错", err)
		}
		d, _ := json.Marshal(msg)

		myLog.InfoLog.Info("服务端写数据", zap.String("response", string(d)))
		myLog.ErrorLog.Error("服务端写数据", zap.String("response", string(d)))
		//log.Println("服务端写数据",string(d))
	}
}

var reqBodyPool = sync.Pool{
	New: func() interface{} {
		return &ReqBody{}
	},
}

func resetReqBody(body *ReqBody) {
	body.Name = ""
	body.Seq = 0
	body.Msg = nil
}

// 接收客户端数据流程
/*
注意点：
如果读取失败（如断连），会触发 Close()。
如果加密失败，会触发 Handshake()，引导客户端重新获取秘钥。
优化
若前端恶意发大量无效数据，是否有防刷机制（频率限制）
*/
func (w *wsServer) readMsgLoop() {
	defer func() {
		if err := recover(); err != nil {
			log.Printf("服务端捕捉到异常: %v\n%s", err, debug.Stack())
			w.Close()
		}
	}()

	// 设置最大读取限制 & 超时控制
	w.wsConn.SetReadLimit(2 << 20) // 2MB
	_ = w.wsConn.SetReadDeadline(time.Now().Add(60 * time.Second))

	for {
		_, data, err := w.wsConn.ReadMessage()
		if err != nil {
			log.Println("收消息出现错误:", err)
			break
		}

		// 解压优化：使用 sync.Pool 缓存 gzip.Reader
		data, err = utils.UnZipWithPool(data)
		if err != nil {
			log.Println("解压失败：", err)
			continue
		}

		// 解密优化：避免频繁转换 key
		var keyBytes []byte
		if secretKey, err := w.GetProperty("secretKey"); err == nil {
			keyStr := secretKey.(string)
			keyBytes = []byte(keyStr)

			data, err = utils.AesCBCDecrypt(data, keyBytes, keyBytes, openssl.ZEROS_PADDING)
			if err != nil {
				log.Println("解密失败：", err)
				w.Handshake()
				continue
			}
		}

		// JSON反序列化优化：结构体对象复用
		body := reqBodyPool.Get().(*ReqBody)
		resetReqBody(body)

		err = json.Unmarshal(data, body)
		if err != nil {
			log.Println("JSON解析失败：", err)
			reqBodyPool.Put(body)
			continue
		}

		// 构建消息对象
		req := &WsMsgReq{Conn: w, Body: body}
		rsp := &WsMsgRsp{Body: &RspBody{Name: body.Name, Seq: body.Seq}}

		// 特殊处理心跳
		if body.Name == "heartbeat" {
			h := &Heartbeat{}
			mapstructure.Decode(body.Msg, h)
			h.STime = time.Now().UnixNano() / 1e6
			rsp.Body.Msg = h
		} else {
			w.router.Run(req, rsp)
		}

		// 非阻塞写出：避免 outChan 阻塞
		w.outChan <- rsp

	}

	w.Close()
}

func (w *wsServer) Close() {
	_ = w.wsConn.Close()
}

const HandshakeMsg = "handshake"

// 加密秘钥协商流程 客户端连接建立后，后端下发 secretKey，以后双方通信基于这个 key 进行对称加密（AES-CBC 模式）
func (w *wsServer) Handshake() {
	secretKey := ""
	key, err := w.GetProperty("secretKey")
	if err == nil {
		secretKey = key.(string)
	} else {
		secretKey = utils.RandSeq(16)
	}
	handshake := &Handshake{Key: secretKey}

	body := &RspBody{Name: HandshakeMsg, Msg: handshake}

	if data, err := json.Marshal(body); err == nil {
		if secretKey != "" {
			w.SetProperty("secretKey", secretKey)
		} else {
			w.RemoveProperty("secretKey")
		}
		if data, err := utils.Zip(data); err == nil {
			w.wsConn.WriteMessage(websocket.BinaryMessage, data)
		}
	}
}
